<template>
  <div class="writecenter-content" @click="popovlerVisibe = false">
    <client-only>
      <server-loading slot="placeholder"></server-loading>
      <div class="writemodel-title">
        <input
          v-model="articleInfo.articleTitle"
          class="writemodel-title-input"
          type="text"
          placeholder="此处请输入文章标题..."
        />
        <a-popover placement="bottomRight" trigger="click" :visible="popovlerVisibe">
          <template slot="content">
            <div class="popover-form" @click.stop>
              <a-form-model
                ref="articleForm"
                :model="articleInfo"
                :rules="rules"
                :label-col="{ span: 6 }"
                :wrapper-col="{ span: 18 }"
              >
                <a-form-model-item label="文章分类" prop="category">
                  <a-select
                    v-model="articleInfo.category"
                    placeholder="请选择分类"
                    @change="handlerCatagoryUpdate"
                  >
                    <a-select-option
                      v-for="item in categoryOption"
                      :key="item.value"
                      :value="item.value"
                    >
                      {{ item.label }}
                    </a-select-option>
                  </a-select>
                </a-form-model-item>
                <a-form-model-item label="标签" prop="tag">
                  <a-select
                    v-model="articleInfo.tag"
                    mode="multiple"
                    allow-clear
                    show-search
                    placeholder="请选择标签"
                    :filter-option="filterTagOption"
                  >
                    <a-select-opt-group
                      v-for="(item, index) in userTagOptions"
                      :key="index">
                      <span slot="label">{{ item.label }}
                        <em
                          @click="tagModalVisible = true; popovlerVisibe = false"
                          v-show="item.showBtn"
                          style="font-style: normal; float: right; color: #2080f7; cursor: pointer;">
                          添加自定义标签
                        </em>
                      </span>
                      <a-select-option
                        v-for="itemName in item.value"
                        :key="itemName.value"
                        :disabled="articleInfo.tag?.length > 3"
                        :value="itemName.value"
                      >
                        {{ itemName.label }}
                      </a-select-option>
                    </a-select-opt-group>
                  </a-select>
                </a-form-model-item>
                <a-form-model-item label="文章封面" style="margin-bottom: 0">
                  <a-upload
                    name="file"
                    list-type="picture-card"
                    class="article-cover-uploader"
                    :show-upload-list="false"
                    action="https://image.zdxblog.cn/upload/uploadFile"
                    :before-upload="beforeImageUpload"
                    @change="handleChange"
                  >
                    <img
                      v-if="articleInfo.coverImage"
                      :src="articleInfo.coverImage"
                      alt="avatar"
                    />
                    <div v-else>
                      <a-icon :type="loading ? 'loading' : 'plus'" />
                      <div class="ant-upload-text">Upload</div>
                    </div>
                  </a-upload>
                </a-form-model-item>
                <a-form-model-item label="参考链接" prop="linkUrl">
                  <a-input
                    v-model="articleInfo.linkUrl"
                    placeholder="请输入文章参考链接"
                  />
                </a-form-model-item>
                <a-form-model-item label="文章描述" prop="description">
                  <a-textarea
                    v-model="articleInfo.description"
                    :max-length="255"
                    placeholder="请输入文章描述"
                    :autoSize="{ minRows: 4, maxRows: 10 }"
                  />
                </a-form-model-item>
                <a-form-model-item
                  label=" "
                  :colon="false"
                  style="margin-bottom: 0; text-align: right"
                >
                  <a-button
                    :disabled="
                      !articleInfo.articleTitle || !articleInfo.content
                    "
                    type="primary"
                    :loading="saveLoading"
                    @click="saveShareArticle"
                    >确认并发布</a-button
                  >
                </a-form-model-item>
              </a-form-model>
            </div>
          </template>
          <a-button
            type="primary"
            @click.stop="popovlerVisibe = !popovlerVisibe">提 交</a-button>
        </a-popover>
      </div>
      <div class="markdown-body-editor" style="position: relative;">
        <server-loading
          v-show="analyzeLoading"
          slot="placeholder"
          tip="正在解析图片地址 . . ."></server-loading>
        <mavon-editor
          ref="md"
          v-if="markdownVisible"
          v-model="articleInfo.content"
          previewBackground="transparent"
          :tab-size="4"
          :ishljs="true"
          :external-link="externalLink"
          :toolbars="toolbars"
          :code-style="codeStyle"
          :box-shadow="false"
          @imgAdd="uploadImageServer"
          @change="handlerMarkdownUpdate"
        >
          <template v-slot:left-toolbar-after>
            <a-dropdown placement="bottomCenter">
              <span class="iconfont icon-theme"></span>
              <template #overlay>
                <a-menu class="code-highlight-stylelist">
                  <a-menu-item-group title="Markdown主题">
                    <a-menu-item
                      :class="{ active: item === theme}"
                      @click="updateMarkdownTheme(item)"
                      v-for="item in themeList"
                      :key="item">
                      <p>{{ item }}</p>
                      <i class="ri-check-double-line"></i>
                    </a-menu-item>
                  </a-menu-item-group>
                </a-menu>
              </template>
            </a-dropdown>
            <a-dropdown placement="bottomCenter">
              <span class="iconfont icon-high-light"></span>
              <template #overlay>
                <a-menu class="code-highlight-stylelist">
                  <a-menu-item-group title="代码高亮样式">
                    <a-menu-item
                      :class="{ active: item === codeStyle}"
                      @click="codeStyle = item"
                      v-for="item in codeStyleList"
                      :key="item">
                      <p>{{ item }}</p>
                      <i class="ri-check-double-line"></i>
                    </a-menu-item>
                  </a-menu-item-group>
                </a-menu>
              </template>
            </a-dropdown>
          </template>
        </mavon-editor>
      </div>
      <div class="article-bottom">
        <div>
          <p class="pointer-text" @click="markdownToTop">回到顶部</p>
        </div>
        <div>
          <p>欢迎使用GimiShare</p>
          <p>字数：{{ staticsMarkdownWords.wordCount }}</p>
          <p>行数：{{ staticsMarkdownWords.lineCount }}</p>
        </div>
      </div>
    </client-only>
    <CustomTagModal
      v-if="tagModalVisible"
      @close-modal="closeTagModal" />
  </div>
</template>

<script>
import axios from 'axios'
import { removeScriptAndLink, getImageLinksFromMD } from '~/util'
import ServerLoading from './components/ServerLoading'
import CustomTagModal from '~/components/CustomTagModal.vue'
import { categoryOption, tagOptions, mavonEditorToolbars, codeStyleList, themeList } from '~/config/optionMap'
export default {
  name: 'WriteCenter',
  components: { ServerLoading, CustomTagModal },
  data () {
    return {
      markdownVisible: false,
      id: this.$route.params.id,
      theme: 'juejin-markdown',
      codeStyle: 'github',
      initTitle: null,
      themeList,
      codeStyleList,
      toolbars: mavonEditorToolbars,
      externalLink: {
        markdown_css: () => '/md/markdown/' + this.theme + '.min.css',
        hljs_js: () => '/md/highlightjs/highlight.min.js',
        hljs_css: (css) => '/md/highlightjs/styles/' + css + '.min.css',
        hljs_lang: (lang) => '/md/highlightjs/languages/' + lang + '.min.js',
        katex_css: () => '/md/katex/katex.min.css',
        katex_js: () => '/md/katex/katex.min.js'
      },
      articleInfo: {
        content: '',
        category: undefined,
        tag: [],
        coverImage: null,
        linkUrl: null,
        description: null
      },
      loading: false,
      categoryOption,
      tagOptions,
      saveLoading: false,
      popovlerVisibe: false,
      tagModalVisible: false,
      customTagList: [],
      analyzeLoading: false,
      rules: {
        category: [
          { required: true, message: '请选择文章分类', trigger: 'change' }
        ],
        tag: [{ required: true, message: '请输入标签', trigger: 'change' }],
        description: [
          { required: true, message: '请输入文章描述', trigger: 'change' }
        ]
      }
    }
  },

  head () {
    return {
      title: !this.initTitle ? '写作中心-写文章' : `写作中心-${this.initTitle}`
    }
  },

  created () {
    this.findWriterCustomTags()
    if (this.id) {
      this.findArticleDetail()
    } else {
      this.markdownVisible = true
    }
  },

  methods: {
    async requestAnalyzeHttpPic (pasteText) {
      const imageLinks = getImageLinksFromMD(pasteText)
      if (imageLinks.length) {
        try {
          this.analyzeLoading = true
          const analyzedHttpLinks = (await this.$axios.post('/upload/analyzeHttp', {
            urls: imageLinks
          })).data
          for (let i = 0; i < analyzedHttpLinks.length; i++) {
            if (analyzedHttpLinks[i]) {
              this.articleInfo.content = this.articleInfo.content.replace(imageLinks[i], analyzedHttpLinks[i])
            }
          }
        } finally {
          this.analyzeLoading = false
        }
      }
    },

    async findArticleDetail () {
      try {
        const { data } = await this.$axios.get(`/gimishare/api/v1/articles/mdinfo?id=${this.id}`)
        this.initTitle = data.articleTitle
        this.articleInfo = {
          ...data,
          tag: data.tag.split(';')
        }
        this.theme = data.theme
        this.codeStyle = data.codeStyle
        this.externalLink = {
          markdown_css: () => '/md/markdown/' + this.theme + '.min.css',
          hljs_js: () => '/md/highlightjs/highlight.min.js',
          hljs_css: (css) => '/md/highlightjs/styles/' + css + '.min.css',
          hljs_lang: (lang) => '/md/highlightjs/languages/' + lang + '.min.js',
          katex_css: () => '/md/katex/katex.min.css',
          katex_js: () => '/md/katex/katex.min.js'
        }
        this.markdownVisible = true
      } catch (error) {
        this.markdownVisible = true
      }
    },

    async findWriterCustomTags () {
      try {
        const data = await this.$axios.get('/gimishare/api/v1/tag')
        this.customTagList = (data.data || []).map(o => {
          return {
            ...o,
            label: o.tagName,
            value: o.tagName
          }
        })
      } catch (error) {
        console.log(error)
      }
    },

    handlerCatagoryUpdate () {
      this.articleInfo.tag = []
    },

    updateMarkdownTheme (theme) {
      const href = '/md/markdown/' + this.theme + '.min.css'
      const themes = document.querySelectorAll("link[href='" + href + "']")
      this.theme = theme
      if (themes?.length) {
        themes[0].href = '/md/markdown/' + this.theme + '.min.css'
      }
    },

    saveShareArticle () {
      this.$refs.articleForm.validate(async (valid) => {
        if (valid) {
          this.saveLoading = true
          try {
            const API = this.id
              ? '/gimishare/api/v1/articles/updatearticle'
              : '/gimishare/api/v1/articles/createarticle'
            const searchParams = {
              ...this.articleInfo,
              tag: this.articleInfo.tag.join(';'),
              theme: this.theme,
              codeStyle: this.codeStyle,
              html: this.$refs.md.d_render
            }
            await this.$axios.post(API, searchParams)
            this.$message.success(this.id ? '文章编辑成功' : '创作文章成功，请等待文章审核')
            !this.id && this.$router.push('/')
            this.popovlerVisibe = false
          } catch (error) {
            console.log(error)
          } finally {
            this.saveLoading = false
          }
        }
      })
    },

    uploadImageServer (pos, $file) {
      const formdata = new FormData()
      formdata.append('file', $file)
      formdata.append('waterText', '1')
      axios({
        url: 'https://image.zdxblog.cn/upload/uploadFile',
        method: 'post',
        data: formdata,
        headers: { 'Content-Type': 'multipart/form-data' }
      })
        .then((data) => {
          this.$refs.md.$img2Url(pos, data.data.data)
        })
        .catch(() => {
          this.$message.error('上传图片失败，请重新上传')
        })
    },

    closeTagModal (render) {
      this.tagModalVisible = false
      if (render) {
        this.findWriterCustomTags()
      }
    },

    beforeImageUpload (file) {
      const fileType = file.name
        .substring(file.name.lastIndexOf('.') + 1)
        .toLowerCase()
      if (!['jpeg', 'jpg', 'svg', 'gif', 'png', 'webp'].includes(fileType)) {
        this.$message.error('上传图片格式不正确')
        return false
      }
      const isLt5M = file.size / 1024 / 1024 < 5
      if (!isLt5M) {
        this.$message.error('上传图片大小不能超过 5MB!')
        return false
      }
      return true
    },

    handleChange (info) {
      if (info.file.status === 'uploading') {
        this.loading = true
        return
      }
      if (info.file.status === 'done') {
        this.articleInfo.coverImage = info.file.response.data
        this.loading = false
      }
      if (info.file.status === 'error') {
        this.$message.error('上传封面图失败')
        this.loading = false
      }
    },

    handlerMarkdownUpdate (c) {
      this.articleInfo.content = c
    },

    markdownToTop () {
      const e = document.querySelector('.v-note-edit')
      const s = document.querySelector('.v-note-show')
      e.scrollTop = 0
      s.scrollTop = 0
    },

    filterTagOption (input, option) {
      return option.componentOptions.children[0].text?.toLowerCase().indexOf(input.toLowerCase()) >= 0
    }
  },

  computed: {
    staticsMarkdownWords () {
      const markdownText = this.articleInfo.content
      if (!markdownText) {
        return {
          lineCount: 0,
          wordCount: 0
        }
      }
      const lineCount = markdownText.split(/\r\n|\r|\n/).length
      const wordCount = markdownText.split(/\s+/).length
      const chineseCharCount = markdownText.trim().replace(/[^\u4e00-\u9fa5]/g, '').length
      return {
        lineCount,
        wordCount: wordCount + chineseCharCount
      }
    },

    userTagOptions () {
      const cutomTags = this.customTagList.filter(
        (i) => i.pid === this.articleInfo.category
      )
      return [
        {
          label: '自定义标签',
          value: cutomTags,
          showBtn: cutomTags?.length
        },
        {
          label: '系统标签',
          value: this.tagOptions.filter(
            (i) => i.pid === this.articleInfo.category
          ),
          showBtn: !cutomTags?.length
        }
      ]
    }
  },

  watch: {
    markdownVisible (newVal) {
      if (newVal) {
        this.$nextTick(() => {
          document.querySelector('.auto-textarea-input').onpaste = (event) => {
            const clipboardData = event.clipboardData || window.clipboardData
            const textContent = clipboardData.getData('Text')
            this.requestAnalyzeHttpPic(textContent)
          }
        })
      }
    }
  },

  beforeDestroy () {
    removeScriptAndLink()
  }
}
</script>

<style scoped lang="less">
.writecenter-content {
  height: 100vh;
  display: flex;
  flex-direction: column;
  .writemodel-title {
    background: #fff;
    padding: 6px 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-bottom: 1px solid #f1f1f1;
    &-input {
      outline: none;
      font-size: 16px;
      border: none;
      flex: 1;
      width: 0;
      color: #000;
      &::placeholder {
        font-size: 14px;
      }
    }
    ::v-deep.ant-btn {
      flex-shrink: 0;
    }
  }
  & > .markdown-body-editor {
    height: 0;
    flex: 1;
    .v-note-wrapper {
      height: 100%;
      z-index: 999 !important;
      span.iconfont {
        margin-left: 12px;
        cursor: pointer;
        color: #000;
      }
      :deep(.v-note-panel) {
        .v-note-edit, .v-note-show {
          border-right: 1px solid #f1f1f1;
          overflow-y: auto;
          cursor: default;
          &::-webkit-scrollbar {
            width: 4px !important;
          }
          &::-webkit-scrollbar-thumb {
            width: 4px;
            background-color: rgb(201, 205, 212) !important;
          }
          &::-webkit-scrollbar-track {
            background-color: #fff;
          }
          .v-show-content {
            height: auto;
            overflow: hidden;
          }
          .content-input-wrapper {
            padding: 15px 80px;
          }
          .v-show-content {
            padding: 0;
            position: relative;
            width: calc(100% - 160px);
            margin: 15px auto;
          }
        }
        .v-note-show {
          border-right: none;
        }
      }
    }
  }
  .article-bottom {
    height: 24px;
    border-top: 1px solid #f1f1f1;
    background: #fff;
    overflow: hidden;
    display: flex;
    padding: 0 10px;
    justify-content: space-between;
    & > div {
      display: flex;
      p {
        font-size: 12px;
        color: #000;
        line-height: 24px;
        padding: 0 6px;
        &.pointer-text {
          cursor: pointer;
          &:hover {
            color: #2080f7;
          }
        }
      }
    }
  }
}
.popover-form {
  width: 400px;
  padding: 20px;
  ::v-deep.ant-form-item {
    margin-bottom: 10px;
  }
}
</style>

<style lang="less">
.article-cover-uploader {
  & > .ant-upload {
    width: 128px;
    height: auto;
    max-height: 200px;
    min-height: 65px;
    .ant-upload {
      padding: 0;
      img {
        width: 100%;
      }
      .anticon {
        font-size: 20px;
        margin-top: 10px;
      }
      .ant-upload-text {
        margin-top: 0;
        margin-bottom: 10px;
        font-size: 12px;
      }
    }
  }
}
</style>
